﻿using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using Wicture.DbRESTFul;

namespace Wicture.EPC.OnlineQueryProxy.Agents
{
    public class AgentManager : IAgentManager
    {
        private bool onlineStatusChanged = true;

        private static ReaderWriterLockSlim readerWriterLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

        private List<AgentState> initialAgentStates;

        public AgentManager()
        {
            initialAgentStates = LoadStates();
        }

        private void UpdateWeightRange(string processorName)
        {
            // 更新权重区间
            if (onlineStatusChanged)
            {
                SchedulerManager.UpdateWeightRange(AgentCollection.Agents, processorName);
                onlineStatusChanged = false;
            }
        }

        public void AddOrUpdate(Agent request)
        {
            readerWriterLock.EnterWriteLock();
            var agent = AgentCollection.Agents.FirstOrDefault(a => a.Id == request.Id);

            if (agent == null)
            {
                agent = new Agent
                {
                    ConnectionId = request.ConnectionId,
                    Name = request.Name,
                    Id = request.Id,
                    GroupName = request.GroupName,
                    ProcessorName = request.ProcessorName,
                    IsReady = request.IsReady,
                    Weight = request.Weight,
                    Enabled = initialAgentStates?.FirstOrDefault(a => a.Id == request.Id)?.Enabled == false ? false : true,
                    IsOnline = true
                };

                AgentCollection.Agents.Add(agent);
                AgentCollection.Agents.Sort();
                onlineStatusChanged = true;

                LoggerManager.Logger.LogInformation($"代理服务器 {request.Name} - {request.Id} 已加入。");
            }
            else if (request.IsReady != agent.IsReady) // 自动重启下线
            {
                agent.IsReady = request.IsReady;
                agent.IsOnline = request.IsReady;
                onlineStatusChanged = true;
                LoggerManager.Logger.LogInformation($"代理服务器 {request.Name} - {request.Id} {(request.IsReady ? "已就绪" : "未就绪")}。");
            }
            else if (request.ForceOffline && agent.IsOnline) // 强制下线
            {
                agent.Weight = request.Weight;
                agent.Name = request.Name;
                agent.IsOnline = false;
                onlineStatusChanged = true;
                LoggerManager.Logger.LogInformation($"代理服务器 {request.Name} - {request.Id} 已下线。");
            }
            else if (agent.Weight != request.Weight)
            {
                LoggerManager.Logger.LogInformation($"代理服务器 {request.Name} - {request.Id} 权重已重置：{agent.Weight} -> {request.Weight}。");
                agent.Weight = request.Weight;
                onlineStatusChanged = true;
            }
            else
            {
                if (!agent.IsOnline)
                {
                    LoggerManager.Logger.LogInformation($"代理服务器 {request.Name} - {request.Id} 已上线。");
                }

                onlineStatusChanged = !agent.IsOnline; // 如果之前是非在线状态，需要更新
                agent.IsOnline = true;
            }

            readerWriterLock.ExitWriteLock();

            agent.ConnectionId = request.ConnectionId;
            agent.Name = request.Name;
            agent.LastConnectedTime = DateTime.Now;

            UpdateWeightRange(agent.ProcessorName);
        }

        public bool Reconnect(string connectionId)
        {
            var agent = GetAgent(connectionId);
            if (agent == null)
            {
                LoggerManager.Logger.LogInformation($"未找到代理服务器 ConnectionId：{connectionId}。");
                return false;
            }

            if (agent.IsOnline == false)
            {
                agent.IsOnline = true;
                onlineStatusChanged = true;
                UpdateWeightRange(agent.ProcessorName);
            }

            LoggerManager.Logger.LogInformation($"代理服务器 {agent.Name} - {agent.Id} 已自动重连。");

            return true;
        }

        public void ForceOffline(string connectionId)
        {
            var agent = GetAgent(connectionId);
            if (agent?.IsOnline == true)
            {
                agent.IsOnline = false;
                LoggerManager.Logger.LogInformation($"代理服务器 {agent.Name} - {agent.Id} 已下线。");
                onlineStatusChanged = true;
                UpdateWeightRange(agent.ProcessorName);
            }
        }

        public Agent GetAgent(string connectionId)
        {
            return AgentCollection.Agents.FirstOrDefault(a => a.ConnectionId == connectionId);
        }

        public AjaxResult Update(UpdateAgentRequest request)
        {
            var agent = AgentCollection.Agents.FirstOrDefault(a => a.Id == request.Id);
            if (agent != null)
            {
                LoggerManager.Logger.LogInformation($"Enabled of Agent {agent.Name} is updated: {agent.Enabled} -> {request.Enabled}");
                agent.Enabled = request.Enabled;

                onlineStatusChanged = true;
                UpdateWeightRange(agent.ProcessorName);

                return AjaxResult.SetResult(string.Empty);
            }
            else
            {
                var message = $"Unabled to find any agent id: {request.Id}";
                LoggerManager.Logger.LogInformation(message);
                return AjaxResult.SetError(message, "606");
            }
        }

        public Agent GetAgentByProcessorName(string processorName)
        {
            return SchedulerManager.GetNext(AgentCollection.Agents, processorName);
        }

        public void SaveStates()
        {
            var path = Path.Combine(Directory.GetCurrentDirectory(), "agent-states.json");

            try
            {
                File.WriteAllText(path, JsonConvert.SerializeObject(AgentCollection.Agents.Select(a => new AgentState(a)), Formatting.Indented));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError("Save states failed." + ex);
            }
        }

        private List<AgentState> LoadStates()
        {
            var path = Path.Combine(Directory.GetCurrentDirectory(), "agent-states.json");
            if (!File.Exists(path)) return new List<AgentState>();

            try
            {
                return JsonConvert.DeserializeObject<List<AgentState>>(File.ReadAllText(path));
            }
            catch (Exception ex)
            {
                LoggerManager.Logger.LogError("Load states failed." + ex);
                return new List<AgentState>();
            }
        }
    }
}
